package com.secure.advice;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.TypeUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.secure.bean.SecureRequest;
import com.secure.constant.RequestConstant;
import com.secure.handler.SecureRequestHandler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.Map;

/**
 * 请求体解密增强
 */
@Slf4j
@ControllerAdvice
@RequiredArgsConstructor
public class RequestBodySecureAdvice implements RequestBodyAdvice {

    private final ObjectMapper objectMapper;
    private final SecureRequestHandler secureRequestHandler;

    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return secureRequestHandler.isNeedSecureRequestForBody(methodParameter.getMethod());
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
        SecureRequest secureRequest = wrapSecureRequest(inputMessage);
        String plainData = secureRequestHandler.decryptAndVerifySignatureRequest(secureRequest);
        if (String.class.equals(TypeUtil.getClass(targetType))) {
            Map<String, String> plainMap = objectMapper.readValue(plainData, new TypeReference<Map<String, String>>() {
            });
            plainData = plainMap.get(RequestConstant.STRING_BODY);
        }

        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(plainData.getBytes(StandardCharsets.UTF_8));
        return new HttpInputMessage() {
            @Override
            public InputStream getBody() throws IOException {
                return byteArrayInputStream;
            }

            @Override
            public HttpHeaders getHeaders() {
                return inputMessage.getHeaders();
            }
        };
    }

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return body;
    }

    @Override
    public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return body;
    }

    /**
     * 安全请求参数封装
     *
     * @param inputMessage body体消息
     * @throws IOException
     */
    private SecureRequest wrapSecureRequest(HttpInputMessage inputMessage) throws IOException {
        String body = IoUtil.read(inputMessage.getBody(), StandardCharsets.UTF_8);
        if (StrUtil.isNotBlank(body)) {
            return objectMapper.readValue(body, SecureRequest.class);
        }
        return null;
    }

}
